/***************************************************************************
 *   Copyright (C) 2009 by Eduardo Gurgel Pinho                            *
 *   edgurgel@gmail.com                                                    *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 3 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
***************************************************************************/

#ifndef __VECTOR_H__
#define __VECTOR_H__

#include <cmath>

namespace Tujchi {

/**
 *  3D Vector class.
 */

struct Vector {
	/**
	 * Simple constructor.
	 * @param _x X value
	 * @param _y Y value
	 * @param _z Z value
	 */
	Vector(const double _x,const double _y,const double _z);

	/**
	 * Simple constructor with z = 0.0
	 * @param _x X value
	 * @param _y Y value
	 */
	Vector(const double _x,const double _y);
	
	/**
	 * Default constructor.
	 * Boost Serialization needs a default constructor
	 * @see Vector(const Vector&)
	 */
	Vector(void);
	
	/**
	 * Copy constructor.
	 */
	Vector(const Vector& _vector);
	
	/**
	 * Destructor
	 */
	~Vector(void);

	/**
	 * Dot product operation.
	 * It is the standard inner product.
	 */
	double dot_product(const Vector& _vector) const;
	
	/**
	 * Scalar Product operation.
	 * This function will be renamed.
	 * @see operator*()
	 */
	Vector scalar_product(double _value) const;
	
	/**
	 * Cross product operation.
	 * Also known as the vector product.
	 * Construct a perpendicular vector from this and _vector.
	 */
	Vector cross_product(const Vector& _vector) const;
	
	/**
	 * Subtraction operation.
	 * @see operator-()
	 */
	Vector sub_vect(const Vector& _vector) const;
	
	/**
	 * Normalization.
	 * A normalized vector is a vector with lenght 1.
	 * @return A normalized vector.
	 */
	Vector normalize(void) const;
	
	/**
	 * Length of a vector.
	 * @return The length.
	 */
	double length(void) const;
	double distance(const Vector& _vector) const;

	double angle(const Vector& _vector) const;
	
	void set(const double _x,const double _y,const double _z);
	
	/**
	 * Print (x,y,z)
	 * Debugging purpose function.
	 */
	void show(void) const;

	Vector& operator=(const Vector&);
	double operator*(const Vector& _vector) const;
	Vector operator*(const double _value) const;
	Vector operator+(const Vector& _vector) const;
	double operator[](const int& _index) const;
	Vector operator-(void) const;
	Vector operator-(const Vector& _vector) const;
	bool operator==(const Vector& _vector) const;
	bool operator<(const Vector& _vector) const;

	double x,y,z; /** x,y,z */
	
};

inline
double Vector::dot_product(const Vector& _vector) const {
	return x*_vector.x+ y*_vector.y+ z*_vector.z;
}

inline
Vector Vector::cross_product(const Vector& _vector) const {
	
	return Vector(y*_vector.z - z*_vector.y, z*_vector.x - x*_vector.z,x*_vector.y - y*_vector.x);
}

inline
Vector Vector::scalar_product(const double _value) const {
	return Vector(x*_value,y*_value,z*_value);
}

inline
Vector Vector::normalize(void) const{
	Vector normalized(*this);
	double mod = normalized.dot_product(normalized);
	if(mod != 0.0) { 
		normalized = normalized.scalar_product(1.0/std::sqrt(mod));
		
	}
	return normalized;
}

inline
Vector Vector::sub_vect(const Vector& _vector) const {
	return Vector(this->x-_vector.x, this->y-_vector.y, this->z-_vector.z);
}

inline
double Vector::length(void) const {
	return std::sqrt(this->dot_product(*this));
}

inline
double Vector::distance(const Vector& _vector) const {
	return std::sqrt((x - _vector.x)*(x - _vector.x) + (y - _vector.y)*(y - _vector.y) + (z - _vector.z)*(z - _vector.z));
}

inline
double Vector::angle(const Vector& _vector) const {
	return std::atan2(_vector.y,_vector.x) - std::atan2(y,x);
}

inline
double Vector::operator*(const Vector& _vector) const {
	return this->dot_product(_vector);
}

inline
Vector Vector::operator*(const double _value) const {
	return this->scalar_product(_value);
}

inline
Vector Vector::operator+(const Vector& _vector) const {
	return Vector(x + _vector.x,y + _vector.y,z + _vector.z);
}

inline
double Vector::operator[](const int& _index) const {
	switch(_index) {
		case 1: return x;
		case 2: return y;
		case 3: return z;
	}
}

inline
Vector Vector::operator-(void) const {
	return Vector(-x,-y,-z);
}

inline
bool Vector::operator==(const Vector& _vector) const {
//	if(x == _vector.x && y == _vector.y && z == _vector.z) return true;
	const double epsilon = 0.0000000001;
	if((std::fabs(x - _vector.x) <= epsilon) && 
	   (std::fabs(y - _vector.y) <= epsilon) && 
		 (std::fabs(z - _vector.z) <= epsilon)) 
		 return true;	
	else return false;
}

inline
bool Vector::operator<(const Vector& _vector) const {
	if(y < _vector.y)
		return true;
	else if(y == _vector.y) {
		if(x > _vector.x)
			return true;
		else 
			return false;
	}
	else
		return false;
}

inline
Vector Vector::operator-(const Vector& _vector) const {
	return sub_vect(_vector);
}


}

#endif
